www.eprace.edu.pl » system-wrzutnikowy » Opis budowy i oprogramowania urządzenia » Komunikacja RS485 Master / Slave - zdarzeniowy automat sekwencyjny

Komunikacja RS485 Master / Slave - zdarzeniowy automat sekwencyjny

Projektując urządzenie podzielone na 2 niezależne bloki funkcjonalne nieodzownym stało się wykorzystanie interfejsu komunikacyjnego do wymiany danych pomiędzy nimi. Ze względu na pewność transmisji i niezawodność (przy zachowaniu odpowiednich uwarunkowań konstrukcyjnych) wybór padł na magistralę RS485. Jest to magistrala Half Duplex charakteryzująca się tym, że na magistrali w danym momencie tylko jedno z urządzeń może być przełączone w tryb nadawania. Załączenie większej ilości nadajników (pomimo ich wewnętrznych zabezpieczeń) skutkować może uszkodzeniem modułów wyjściowych tych układów. Naturalnym jest wykorzystanie w roli konwertera popularnego układu scalonego MAX485, który po stronie TTL może być podłączony bezpośrednio do pinów modułu USART. Układ wyposażony jest również w 2 wejścia sterujące kierunkiem transmisji, które spięte razem są podłączone do jednego pinu mikrokontrolera.

Decydując się na budowę od podstaw funkcji obsługi wymiany danych, autor od początku postawił na zdarzeniowy automat sekwencyjny. Oczywiście istnieją inne, być może lepsze sposoby komunikowania ze sobą urządzeń w sieci RS485. Jednak w tym specyficznym zastosowaniu na uwagę zasługuje prostota obsługi, brak potrzeby używania buforów nadawczych i odbiorczych oraz mała zajętość pamięci FLASH. Program przewidziany jest do komunikowania ze sobą tylko 2 urządzeń. W obecnej formie nie znajdzie więc zastosowania w sieciach, w których pracuje wiele SLAVE'ów 485. Jednak w wybranym zastosowaniu wyśmienicie realizuje swoją funkcję.

Istotą działania algorytmu jest jak najmniejsze zabieranie czasu procesora - cała komunikacja opiera się o zdarzenie "newByte" generowane w przerwaniu od odbioru danych przez UART. Program odbiornika - jest prostym odpowiadaniem na kolejne zapytania z MASTER'a. Zasadę działania odbiornika prezentuje poniższy rysunek:

Rys. 16 - Algorytm komunikacji RS485, odbiornik

Dużo bardziej zaawansowanym algorytmem jest kod nadajnika MASTER. Realizuje on cykliczne odpytywanie SLAVE co zadany interwał czasu. Na początku każdej transmisji ustawiany jest licznik czasu timeout dla całej paczki danych. Następnie, automat dokonuje cyklicznych zapytań SLAVE o zdefiniowany parametr. Po wysłaniu każdego zapytania procedura nie zajmuje niepotrzebnie czasu procesora, oczekując na wystąpienie zdarzenia "newByte". Natychmiast po jego wystąpieniu dana z rejestru wejściowego UDR modułu USART zostaje przepisana do zmiennej użytkownika a automat przechodzi do następnego kroku - wysyła kolejne zapytanie i znów oczekuje na wystąpienie zdarzenia. Jeżeli upłynie czas timeout jest to znak dla procedury, że wystąpił jakiś błąd w komunikacji. W takim przypadku następuje powrót do kroku zerowego automatu i wysyłanie (co zadany interwał) specjalnego symbolu zapytania. Reszta kroków wstrzymana jest do momentu odebrania poprawnej odpowiedzi na symbol testowy. W panelu klienckim wykorzystano informację o problemie z komunikacją do:

- prezentacji komunikatu błędu na wyświetlaczu,

- możliwości restartu panelu klienckiego po zadanym czasie braku komunikacji.

Ze względu na walory użytkowe tego rozwiązania autor zdecydował się na umieszczenie w treści pracy kompletnego kodu nadajnika, co prezentuje poniższy listing:

Listing 3 - Zdarzeniowy automat sekwencyjny do komunikacji RS485

#define CzasOdpytywaniaSterownika 33 //* 10ms (przy założeniu, że korzystasz z tzw sysTick o rozdzielczości 10ms)

#define MaksymalnaIloscBledow 10 //szt.

#define CzasAutoresetTimeout 60 //sek. po tym czasie w moim przypadku generowałem programowy restart urządzenia

#define CzasTimeout CzasOdpytywaniaSterownika-1 //odczytanie wszystkich parametrów musi zostac zrealizowane szybciej niż nastąpi próba kolejnej pełnej transmisji danych, jeżeli jest inaczej - oznacza to timeout transmisji

//zmienne globalne

char licznik_kontrolaTransmisji; //licznik bledow transmisji

volatile char daneRS; //dane odczytane z UART

/* UWAGA! należy utworzyć sobie pole bitowa "flaga" z bitami nazwanymi tak jak poniżej, albo użyć tu zwykłych zmiennych boolowskich zamiast pól bitowych*/

#define bladKomunikacji flaga.f1 //blad komunikacji licznik_kontrolaTransmisji > dop ilosc bledow

u8 zliczanieTimeout; //licznik resetujacy panel po xx sekundach braku komunikacji

#define autorestartTimeout flaga.f2 //restrart po uplywie timeout  

/* Zmienne L_OdpytywanieSPA oraz L_Timeot to zmienne typu uint8_t, timery programowe których obsługa realizowana jest w zdarzeniu "systTick"

funkcja przejsc pomiedzy poszczegolnymi stanami automatu odbiorczego*/

void tranzycjaAutomatu(char *whatSend, char *zmienna, u8 nextPos)

{

/ * Jezeli aktualnie nie jest wysyłane zadne zapytanie to wyslij ządane zapytanie

 * i ustaw flagę informującą o ttrwającym odpytaniu urządzenia

 */

 if(!SendingByte)

 {

 rs_text(whatSend);

 SendingByte=true;

 }

/ * Jeżeli w przerwaniu od UART ustawiona została flaga otrzymania nowego znaku to

 * - skasuj flagę nadejscia

 * - skasuj flagę trwającego odpytania

 * - przepisz daną do żądanej zmiennej

 * - przejdz do następnej pozycji automatu

 */

 if(NewByte)

 {

 SendingByte=false;

 NewByte=false;

 (*zmienna)=daneRS;

 AutomatOdbiorczy=nextPos;

 NextPos=true;

 }

return;

}

inline void communication_task(void) __attribute__((always_inline));

inline void communication_task(void)

{

char tranctrl;

/ * Jeżeli automat wszedl do kroku wyższego od 0, ale do uart nie trafia zadna odpowiedz ze sterownika

 * to zaprzestan wykonywanie aktualnego kroku i powroc do kroku 0 - wysylanie zapytan kontrolnych

 */

if(!L_Timeot&&SendingByte)

{

 SendingByte=false;

 AutomatOdbiorczy=0;

 licznik_kontrolaTransmisji++;

}

/ * Rozpocznij nową procedurę odpytywania jeżeli wyzerował się licznik

 * częstotliwosci zapytan lub wejdz do aktualnej procedury jezeli uart odebral jaks znak

 */

if(!L_OdpytywanieSPA||NewByte||NextPos)

{

/ * Jezeli to uart odebral znak, a nie nastąpilo zdarzenie licznikowe

 * to nie zapisuj na nowo czasu odpytywania - potrzebne do realizacji timeout

 */

//if(!NewByte)

L_OdpytywanieSPA=CzasOdpytywaniaSterownika;

if(NextPos)

NextPos=false;

/ / Sekwencyjny automat odbiorczy realizujacy kolejne zapytania

switch(AutomatOdbiorczy)

{

case 0://sprawdzenie komunikacji

 /* Dzialanie analogiczne do funkcji "tranzycjaAutomatu" z tą różnicą że:

 * - jeżeli odebrany znak odpowiada znakowi zadeklarowanemu jako odpowiedz to przejdz do nastepnego kroku automatu

 * - jeżeli odebrany znak jest inny to inkrementuj licznik bledow i pozostan w kroku 0

 * - jezeli licznik bledów > zadeklarowanej ilosci bledow to zglos bląd transmisji,

 * zaprzestan odpytywania kolejnych parametrow i pozostan w kroku 0 do czasu odebrania poprawnej odpowiedzi

 * na zapytanie testowe*/

 L_Timeot=CzasTimeout;

 tranzycjaAutomatu("?",&tranctrl,1);

 if(bladKomunikacji)

 AutomatOdbiorczy=0;

break;

case 1://odczyt kredytu

 tranzycjaAutomatu("k",&AktualnyKredytZl,15);

break;

//lista kolejnych zmiennych do odczytu....

case 15://zadanie resetu zdalnego

 tranzycjaAutomatu("r",&zrestartujSPA,0);

 L_Timeot=CzasTimeout;

 NextPos=false;

break;

}

if(AutomatOdbiorczy<2)

{

if((tranctrl=='+')&&(L_Timeot))

 licznik_kontrolaTransmisji=0;

if((tranctrl!='+')||(!L_Timeot))

 licznik_kontrolaTransmisji++;

tranctrl=0;

}

}

if(licznik_kontrolaTransmisji>=MaksymalnaIloscBledow)

{

 bladKomunikacji=true;

 licznik_kontrolaTransmisji=MaksymalnaIloscBledow;

}

else

 bladKomunikacji=false;

/ / Przy długotrwałym utrzymywaniu się błędu komunikacji zainicjalizuj procedurę restartu panelu

if(bladKomunikacji)

{

 AutomatOdbiorczy=0;

 if(!L_TimeotAutoreset)

 {

 L_TimeotAutoreset=100;

 zliczanieTimeout++;

 if(zliczanieTimeout>CzasAutoresetTimeout)

 autorestartTimeout=true;

//prewencyjne czyszczenie zawartosci zmiennych roboczych

AktualnyKredytZl=0;

//lista zmiennych do skasowania......

 }

}

else

{

 zliczanieTimeout=0;

}

return;

}

Dzięki stworzeniu funkcji tranzycjaAutomatu() modernizacja kodu polegająca na dodaniu nowego parametru do odczytywania z modułu SLAVE sprowadza się tylko do modyfikacji konstrukcji SWITCH - CASE w kodach nadajnika i odbiornika. Powyższy kod nie wymaga buforowania, ponieważ każde kolejne zapytanie możliwe jest dopiero po uzyskaniu odpowiedzi na zapytanie wcześniejsze (funkcja tranzycjaAutomatu()). Prostota tego rozwiązania pozwala na umieszczenie w głównej pętli programu funkcji communication_task (), która zadba o poprawną wymianę danych z urządzeniem odpytywanym bez absorbowania do tego celu innych zasobów.



komentarze

Copyright © 2008-2010 EPrace oraz autorzy prac.